package main

import "fmt"

type Person struct {
	Name string
	Sexual string
	Age   int
}

type Country struct {
	Name string
}
type City struct {
	Name string
}
type Printable interface {
	PrintStr()
}

type Stringable interface {
	ToString() string
}

func (c Country) ToString() string {
	return "Country = " + c.Name
}
func (c City) ToString() string{
	return "City = " + c.Name
}

func PrintStr(p Stringable)  {
	fmt.Println(p.ToString())
}

/*
	引入一个叫 WithName的结构体，然而，所带来的问题就是，在初始化的时候，变得有点乱。那么，有没有更好的方法？
 */
type WithName struct {
	Name string
}

type Country02 struct {
	WithName
}
type City02 struct {
	WithName
}

func (w WithName) PrintStr()  {
	fmt.Println(w.Name)
}

/*
	使用了一个 Printable 的接口，而 Country 和 City 都实现了接口方法 PrintStr() 而把自己输出。然而，这些代码都是一样的。能不能省掉呢？
	可以使用“结构体嵌入”的方式来完成这个事
 */

func (c Country) PrintStr() {
	fmt.Println(c.Name)
}
func (c City) PrintStr() {
	fmt.Println(c.Name)
}

func main() {

	var p = Person{
		Name: "zhangsan",
		Sexual: "male",
		Age: 18,
	}

	PrintPerson(&p)
	p.Print()

	fmt.Println("-----------------------------------")
	c1 := Country {"China"}
	c2 := City {"Beijing"}
	c1.PrintStr()
	c2.PrintStr()

	fmt.Println("-----------------------------------")

	// 使用嵌入结构体，省略相同的代码
	c3 := Country02 {WithName{"China"}}
	c4 := City02 {WithName{"Beijing"}}
	c3.PrintStr()
	c4.PrintStr()

	fmt.Println("-----------------------------------")

	/*
		使用Stringable的接口，把“业务类型” Country 和 City 和“控制逻辑” Print() 给解耦。
		只要实现了Stringable 接口，都可以传给 PrintStr() 来使用。
	 */
	d1 := Country {"USA"}
	d2 := City{"Los Angeles"}
	PrintStr(d1)
	PrintStr(d2)
	
}

func PrintPerson(p *Person)  {
	fmt.Printf("Name=%s, Sexual=%s, Age=%d\n", p.Name, p.Sexual, p.Age)
}

func (p *Person) Print()  {
	fmt.Printf("Name=%s, Sexual=%s, Age=%d\n", p.Name, p.Sexual, p.Age)
}
